package com.example.lotterysystem.service.impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.param.*;
import com.example.lotterysystem.dao.dataobject.ActivityDO;
import com.example.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.example.lotterysystem.dao.dataobject.ActivityUserDO;
import com.example.lotterysystem.dao.dataobject.PrizeDO;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.service.ActivityService;
import com.example.lotterysystem.service.dto.ActivityDTO;
import com.example.lotterysystem.service.dto.ActivityDetailDTO;
import com.example.lotterysystem.service.dto.CreateActivityDTO;
import com.example.lotterysystem.service.dto.PageListDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import com.example.lotterysystem.service.enums.ActivityUserStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ActivityServiceImpl implements ActivityService {
    /**
     * 活动缓存前置(用于区分业务)
     */
    private final String ACTIVITY_PREFIX = "ACTIVITY_";

    /**
     * 活动缓存过期时间：3天
     */
    private final Long ACTIVITY_TIMEOUT = 60 * 60 * 24 * 3L;

    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private RedisUtil redisUtil;

    /**
     * 创建抽奖活动
     * @param param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class) // 涉及多表，当发生异常时需要进行回滚
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        // 参数校验
        checkActivityInfo(param);

        // 创建抽奖活动
        ActivityDO activityDO = new ActivityDO(); // 构造 dao 层请求
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);

        // 关联活动奖品
        List<ActivityPrizeParam> activityPrizeParams = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeParams
                .stream()
                .map(activityPrizeParam -> {
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeId(activityPrizeParam.getPrizeId());
                    activityPrizeDO.setPrizeAmount(activityPrizeParam.getPrizeAmount());
                    activityPrizeDO.setPrizeTiers(activityPrizeParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);

        // 关联活动人员
        List<ActivityUserParam> activityUserParamList = param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList = activityUserParamList
                .stream()
                .map(activityUserParam -> {
                    ActivityUserDO activityUserDO = new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(activityUserParam.getUserId());
                    activityUserDO.setUserName(activityUserParam.getUserName());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDO;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);

        // 整合活动、奖品 和 人员完整信息, 并将完整信息存放在 redis 中
        List<Long> prizeIds = param.getActivityPrizeList()
                .stream()
                .map(ActivityPrizeParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList()); // 获取需要查询的奖品 id

        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds); // 从数据库中查询奖品信息
        // 构造 ActivityDetailDTO
        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(activityDO,
                activityUserDOList, prizeDOList, activityPrizeDOList);

        // 将 ActivityDetailDTO 缓存到 redis 中
        cacheActivity(activityDetailDTO);

        // 构造响应并返回
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;
    }

    /**
     * 将 activityDetailDTO 缓存到 redis 中
     * @param activityDetailDTO
     */
    private void cacheActivity(ActivityDetailDTO activityDetailDTO) {
        if (null == activityDetailDTO || null == activityDetailDTO.getActivityId()) {
            log.warn("缓存活动不存在！");
            return;
        }
        try {
            redisUtil.set(ACTIVITY_PREFIX + activityDetailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(activityDetailDTO),
                    ACTIVITY_TIMEOUT);
        } catch (Exception e) {
            log.error("缓存活动异常, activityDetailDTO: {}",
                    JacksonUtil.writeValueAsString(activityDetailDTO), e);
        }
    }

    /**
     * 查询抽奖活动列表
     * @param param
     * @return
     */
    @Override
    public PageListDTO<ActivityDTO> findActivityPageList(PageParam param) {
        // 参数校验
        if (null == param) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_FIND_LIST_PARAM_IS_EMPTY);
        }
        // 查询数据总量
        int total = activityMapper.count();
        // 查询当前页数据
        List<ActivityDO> activityDOList = activityMapper.selectActivityList(param.getOffset(), param.getPageSize());
        // 类型转化 ActivityDO -> ActivityDTO
        List<ActivityDTO> activityDTOList = activityDOList
                .stream()
                .map(activityDO -> {
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setDescription(activityDO.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
                    return activityDTO;
                }).collect(Collectors.toList());
        return new PageListDTO<>(total, activityDTOList);
    }

    @Override
    public void cacheActivity(Long activityId) {
        if (null == activityId) {
            log.warn("需缓存的活动 id 为空");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }
        ActivityDetailDTO activityDetailDTO = selectFromMysql(activityId);
        // 将活动存放到 redis 中
        cacheActivity(activityDetailDTO);
    }

    /**
     * 从数据库中查询活动详细信息
     * @param activityId
     * @return
     */
    private ActivityDetailDTO selectFromMysql(Long activityId) {
        if (null == activityId) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_ID_IS_EMPTY);
        }
        // 查询活动表
        ActivityDO activityDO = activityMapper.selectById(activityId);
        if (null == activityDO) { // 活动是否存在
            throw new ServiceException(ServiceErrorCodeConstants.GET_ACTIVITY_DETAIL_ERROR);
        }
        // 查询活动人员表
        List<ActivityUserDO> activityUserDOList = activityUserMapper.selectByActivityId(activityId);
        // 查询活动奖品表
        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeMapper.selectByActivityId(activityId);
        // 查询奖品表
        List<Long> prizeIds = activityPrizeDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
        // 整合活动详细信息
        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(activityDO, activityUserDOList,
                prizeDOList, activityPrizeDOList);
        return activityDetailDTO;
    }

    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        // 参数校验
        if (null == activityId) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_ID_IS_EMPTY);
        }
        // 查询 redis 中缓存的活动信息
        ActivityDetailDTO activityDetailDTO = getActivityFromCache(activityId);
        if (null != activityDetailDTO) {
            log.info("查询活动详细信息成功！activityDetailDTO: {}", JacksonUtil.writeValueAsString(activityDetailDTO));
            return activityDetailDTO;
        }
        // redis 中未缓存，查表
        activityDetailDTO = selectFromMysql(activityId);
        // 将活动存放到 redis 中
        cacheActivity(activityDetailDTO);
        return activityDetailDTO;
    }

    /**
     * 根据活动 id 从缓存中获取详细信息
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        // 参数校验
        if (null == activityId) {
            log.warn("获取缓存活动数据 activityId 为空！");
            return null;
        }
        try {
            // 查询 redis
            String activityDetailJson = redisUtil.get(ACTIVITY_PREFIX + activityId);
            // 数据是否存在
            if (!StringUtils.hasText(activityDetailJson)) {
                log.info("获取缓存活动数据为空, key: {}", ACTIVITY_PREFIX +activityId);
                return null;
            }
            return JacksonUtil.readValue(activityDetailJson, ActivityDetailDTO.class);
        } catch (Exception e) {
            log.warn("从缓存中获取活动数据异常, key: {}", ACTIVITY_PREFIX + activityId);
            return null;
        }
    }

    /**
     * 根据基本DO整合完整的活动信息 ActivityDetailDTO
     * @param activityDO
     * @param activityUserDOList
     * @param prizeDOList
     * @param activityPrizeDOList
     * @return
     */
    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<ActivityUserDO> activityUserDOList,
                                                         List<PrizeDO> prizeDOList,
                                                         List<ActivityPrizeDO> activityPrizeDOList) {
        ActivityDetailDTO activityDetailDTO = new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId()); // 设置活动id
        activityDetailDTO.setActivityName(activityDO.getActivityName()); // 设置活动名
        activityDetailDTO.setDesc(activityDO.getDescription()); // 设置活动描述信息
        activityDetailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus())); // 设置活动状态
        // 整合活动人员列表
        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList
                .stream()
                .map(activityUserDO -> {
                    ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                    userDTO.setUserId(activityUserDO.getUserId());
                    userDTO.setUserName(activityUserDO.getUserName());
                    userDTO.setStatus(ActivityUserStatusEnum.forName(activityUserDO.getStatus()));
                    return userDTO;
                }).collect(Collectors.toList());
        activityDetailDTO.setUserDTOList(userDTOList); // 设置活动人员列表
        // 整合活动奖品列表
        // PrizeDO: {id, name, description, price, imageUrl}
        // ActivityPrizeDO: {activityId, prizeId, prizeAmount, tiers, status}
        // 通过 id 进行整合
//        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeDOList
//                .stream()
//                .map(activityPrizeDO -> {
//                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
//                    prizeDTO.setPrizeId(activityPrizeDO.getPrizeId());
//                    Optional<PrizeDO> optionalPrizeDO = prizeDOList
//                            .stream()
//                            .filter(prizeDO -> prizeDO.getId().equals(activityPrizeDO.getPrizeId()))
//                            .findFirst();
//                    // 若 PrizeDO 为空，则不执行当前方法
//                    optionalPrizeDO.ifPresent(prizeDO -> {
//                        prizeDTO.setPrizeName(prizeDO.getName());
//                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
//                        prizeDTO.setPrice(prizeDO.getPrice());
//                        prizeDTO.setDescription(prizeDO.getDescription());
//                    });
//                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(activityPrizeDO.getPrizeTiers()));
//                    prizeDTO.setPrizeAmount(activityPrizeDO.getPrizeAmount());
//                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(activityPrizeDO.getStatus()));
//                    return prizeDTO;
//                }).collect(Collectors.toList());
        // 构建 prizeId 到 PrizeDO 的映射，减少重复查找
        Map<Long, PrizeDO> prizeDOMap = prizeDOList
                .stream()
                .collect(Collectors.toMap(PrizeDO::getId, prizeDO -> prizeDO));
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList = activityPrizeDOList
                .stream()
                .map(activityPrizeDO -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    // 获取对应 PrizeDO
                    PrizeDO prizeDO = prizeDOMap.get(activityPrizeDO.getPrizeId());
                    if (null == prizeDO) {
                        throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
                    }
                    prizeDTO.setPrizeId(prizeDO.getId());
                    prizeDTO.setPrizeName(prizeDO.getName());
                    prizeDTO.setImageUrl(prizeDO.getImageUrl());
                    prizeDTO.setPrice(prizeDO.getPrice());
                    prizeDTO.setDescription(prizeDO.getDescription());
                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(activityPrizeDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(activityPrizeDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(activityPrizeDO.getStatus()));
                    return prizeDTO;
                }).collect(Collectors.toList());
        activityDetailDTO.setPrizeDTOList(prizeDTOList); // 设置活动奖品列表
        return activityDetailDTO;
    }

    /**
     * 校验创建活动参数
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        // 1. 校验活动的奖品是否存在
        List<Long> prizeIds = param.getActivityPrizeList()
                .stream()
                .map(ActivityPrizeParam::getPrizeId)
                .distinct() // 去重
                .collect(Collectors.toList()); // 传入的奖品id
        List<Long> existPrizeIds = prizeMapper.selectExistByIds(prizeIds); // 数据库中存储奖品id
        if (CollectionUtils.isEmpty(existPrizeIds)) { // 数据库中存储奖品信息为空
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(id -> {
            if (!existPrizeIds.contains(id)) { // 传入的奖品id 在数据库中不存在
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });

        // 2. 校验参与活动的人员是否存在
        List<Long> userIds = param.getActivityUserList()
                .stream()
                .map(ActivityUserParam::getUserId)
                .distinct()
                .collect(Collectors.toList()); // 传入的人员id
        List<Long> existUserIds = userMapper.selectExistByIds(userIds); // 数据库中存储的人员 id
        if (CollectionUtils.isEmpty(existUserIds)) { // 查询的人员Id为空
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(id -> {
            if (!existUserIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });

        // 3. 判断奖品数量是否 >= 活动人员数量
        long userCount = param.getActivityUserList().size();
        long prizeCount = param.getActivityPrizeList()
                .stream()
                .mapToLong(ActivityPrizeParam::getPrizeAmount)
                .sum(); // 要计算每个种类的奖品数量的总和
        if (userCount < prizeCount) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_AMOUNT_ERROR);
        }

        // 4. 校验活动奖品有效性
        param.getActivityPrizeList().forEach(prize -> {
            if (null == ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });
    }
}
